# -*- coding: utf-8 -*-

import re
import sys
import codecs

from tekstiapu import Wikitmpl as Tmplcall
import parserifunktiot

class Sym():
    o2 = u"⦃"
    o3 = u"⟨"
    c2 = u"⦄"
    c3 = u"⟩"




class Pos(object):
    def __init__(self, pos, tp, l):
        self.pos = pos
        self.type = tp
        self.length = l
        self.parts = []

    def __str__(self):
        return u"({}, '{}', {}, {})".format(self.pos, self.type, self.length, self.parts)


class Node(object):
    def __init__(self):
        self.children = []
        self.type = 0

    def append(self, e):
        self.children.append(e)

    def open_sym(self):
        return ""

    def close_sym(self):
        return ""

    def print_children(self, params, ind):
        for c in self.children:
            if type(c) == unicode:
                print "{}{}".format(ind*' ', c)
            else:
                c.printt(params, ind)

    def printt(self, params, ind=0):
        print u"{}{}".format(ind*' ', self.open_sym())
        self.print_children(params, ind+2)
        print u"{}{} ".format(ind*' ', self.close_sym())

    def _evaluate(self, params):
        strs = []

        for c in self.children:
            if type(c) == unicode:
                str = c
            else:
                str = c.evaluate(params)
            strs.append(str)
        strs = map(lambda b: b == None and "NONE" or b, strs) # VÄLIAIKAINEN
        content = u"".join(strs)
        return content

    def evaluate(self, params):
#        print "Node.evaluate:params: " + str(params)
        content = self.open_sym() + self._evaluate(params) + self.close_sym()
        return content



class Node2(Node):
    def __init__(self):
        self.type = 2
        super(Node2, self).__init__()

    def open_sym(self):
        return Sym.o2

    def close_sym(self):
        return Sym.c2

    def eval_command(self, cmd, cmdps):
        """
        Parserifunktiot.
        """
        return parserifunktiot.eval(cmd, cmdps)

    def evaluate(self, params):
        content = super(Node2, self)._evaluate(params)
#        print "Node2.evaluate:content(2): " + content
#        print u"Node2.evaluate:params: {}".format(params)

        cpp = content.split(u"|")
        cmd = cpp[0]
        cmdps = cpp[1:]
        pos = cmd.find(u":")
        if pos != -1:
            return self.eval_command(cmd, cmdps)
        else:
            if pos == -1: pos = len(content)
#            print "tmpl: " + cmd
            tmplcall = Tmplcall("{{" + content + "}}")
            try:
                testi = Template(filename=tmplcall.malline, frmt="wtxt")
            except IOError as ioe:
#                print u"Mallinetta ei löydy: " + content
                return self.open_sym() + content.replace(u"|", u"¦") + self.close_sym()

            params = tmplcall.get_tekstiparams()
#            print "params: " + str(params)
            return testi.evaluate(params)


class Node3(Node):
    def __init__(self):
        self.type = 3
        super(Node3, self).__init__()

    def open_sym(self):
        return Sym.o3

    def close_sym(self):
        return Sym.c3

    def evaluate(self, params):
#        print u"Node3.evaluate:params: {}".format(params)
        content = super(Node3, self)._evaluate(params)
#        print "content(3): " + content
        a = content.split(u"|")
#        print "a: " + str(a)
        num = a[0]

        if num in params:
#            print "saatiin " + params[num]
            return params[num]
        else:
            # oletusarvo
            if len(a) > 1:
 #               print "saatiin oletusarvo: " + a[1]
                return a[1]

        # ei pysty evaluoimaan
        return self.open_sym() + num + self.close_sym()




def get_sym(c, l):
    if c == u"{":
        if l == 2:
            return Sym.o2
        elif l == 3:
            return Sym.o3
    elif c == u"}":
        if l == 2:
            return Sym.c2
        elif l == 3:
            return Sym.c3
    return u"[?={},{}]".format(c, l)

def p_to_w(c):
    if c == Sym.o2:
        return "{{"
    elif c == Sym.o3:
        return "{{{"
    elif c == Sym.c2:
        return "}}"
    elif c == Sym.c3:
        return "}}}"
    return c

class Template(object):
    def __init__(self, filename=None, txt=None, frmt="wtxt"):
        self.fn = filename
        self.wtxt = None
        self.ptxt = None
        self.parse_tree = None
        
        if filename:
#            print u"Luetaan: " + self.fn + ", format: " + frmt
            with codecs.open(u"/home/anttik/src/hunsbot/mallineparseri/malline/" + self.fn, u"r", encoding="utf-8") as f:
                txt = f.read()
                
        if txt:
            if frmt == "wtxt":
                self.wtxt = txt
                self.parse_tree = self.parse_wtxt(self.wtxt)
            elif frmt == "ptxt":
                self.ptxt = txt
                self.parse_tree = self.parse_ptxt(self.ptxt)
        
                
    def strip_comments(self, s):
        """
        Poistaa kommentit.
        """
        def strip_x(s, alku, loppu):
            p = 0
            b = 0
            start = 0
            output = []
            while True:
                m = re.search(u"" + alku + u"|" + loppu + u"", s[p:])
                if m == None: break
                if m.group(0) == u"" + alku + u"":
                    if b == 0:
                       output.append(s[start:p+m.start(0)]) 
                    b += 1
                else:
                    b -= 1
                    if b == 0:
                       start = p + m.end(0)
                p += m.end(0)
            output.append(s[start:]) 
            return "".join(output)

        s = strip_x(s, u"<!--", u"-->")
        s = strip_x(s, u"<noinclude>", u"</noinclude>")
        s = re.sub("<includeonly>", "", s)
        s = re.sub("</includeonly>", "", s)
        return s
    
    def parse_wtxt(self, s):
        s = self.strip_comments(s)
#        sys.stdout.write(s.encode('utf-8'))
        p = 0
        arr = []
        while True:
            # Etsitään {{, }}, {{{, }}}, jne.
            m = re.search("([}{])\\1*", s[p:])
    
            if m == None: break
            p += m.start(0)
            l = len(m.group(0))
            arr.append(Pos(p, m.group(1), l))
            p += l
        
        
        # jaetaan kaarisuljeryhmät osiin vastakkaisten perusteella
        st = []
        for e in arr:
#            print e
            if e.type == "{":
                st.append(e)
            elif e.type == "}":
                if len(st) == 0:
                    raise "TODO" # ylimääräinen }-sulje??
                elif st[-1].length > e.length:
                    st[-1].length -= e.length
                    st[-1].parts.append(e.length)
                    if st[-1].length == 0: 
                        st.pop()
                elif e.length >= st[-1].length:
                    while len(st) > 0 and e.length >= st[-1].length:
                        e.length -= st[-1].length
                        e.parts.append(st[-1].length)
                        st.pop()
                        if e.length == 0:
                            break
        
        
        if len(st) > 0 or (len(arr) > 0 and arr[-1].length > 0):
            print "Enemmän auki- kuin kiinni-sulkeita? stack: {}, arr: {}".format(len(st), len(arr))
#            raise "TODO"
    
        # luodaan taulukko, jossa ryhmät on eroteltu
        output = []
        p = 0
        for e in arr:
            output.append(s[p:e.pos])
            p = e.pos
            if e.parts == []:
                output.append(get_sym(e.type, e.length))
                p += e.length
            else:
                for o in e.parts:
                    output.append(get_sym(e.type, o))
                    p += o
        output.append(s[p:])
    
        self.ptxt = u"".join(output)
#        print "ptxt: " + self.ptxt
        return self._parse(output)
    
    def parse_ptxt(self, s):
        """
        Parsii jo parsitun ja muutetun. TODO ei tarkista oikeellisuutta
        """
#        s = self.strip_comments(s)
        p = 0
        output = []
        while True:
            # Etsitään ⦃⦄⟨⟩
            m = re.search(u"[⦃⦄⟨⟩]", s[p:])
            if m == None: break
            output.append(s[p:p+m.start(0)])
            output.append(m.group(0))

            p += m.end(0)
        output.append(s[p:])


        self.wtxt = u"".join(map(p_to_w, output))
        return self._parse(output)

    def _parse(self, parsed):

        #    print u"Tulos: {}".format(u"".join(parsed))
    
        st = []
        st.append(Node())
        for e in parsed:
            if e == Sym.o2:
                n = Node2()
                st[-1].append(n)
                st.append(n)
            elif e == Sym.o3:
                n = Node3()
                st[-1].append(n)
                st.append(n)
            elif e == Sym.c2:
                popd = st.pop()
                assert ( type(popd) == Node2 )
            elif e == Sym.c3:
                popd = st.pop()
                assert ( type(popd) == Node3 )
            else:
                st[-1].append(e)
    
        return st[-1]
    
    def evaluate(self, params):
#        print u"Template.evaluate:params: {}".format(params)
        if self.parse_tree:
            return self.parse_tree.evaluate(params)

    def print_ptxt(self):
        print (self.ptxt.encode('utf-8'))

    def print_wtxt(self):
        print (self.wtxt.encode('utf-8'))
    
if __name__ == "__main__":
    
#    testi = Template(txt=u"⦃testi|hello|parseri⦄⦃testi|hello⦄", frmt="ptxt")
#    testi = Template(filename="testi-en-verbi.txt", format="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-salata|maal}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-salata|hyö|k|kk|ä|y|ö}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-sanoa|kell|||a|u|o|u}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-sanoa|toi|p|v}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-sallia|keks|||ä|y|ö}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-tulla|o|mme|mpe}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-kaivaa|kat|t}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-verbi-taiv-huutaa|huu|t|d}}", frmt="wtxt")
#    testi = Template(txt=u"{{fi-subs-maa|jä|ä|ä}}", frmt="wtxt")
    txt = unicode(sys.argv[1].decode("utf-8"))
    print u"TEKSTI: {}".format(txt)
    testi = Template(txt=txt, frmt="wtxt")
#    print u"**************************"
#    testi.print_wtxt()
#    print u"**************************"

#    params = dict((unicode(i), unicode(sys.argv[i], "utf-8")) for i in xrange(1, len(sys.argv)))
#    params = { "1" : "ha", "2" : u"rm", "3": "aa", "4" : "17" }

    params = {}
    tulos = testi.evaluate(params).encode("utf-8")
    print tulos
 #   print params



